package com.ltmonitor.jt808.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.PostConstruct;

import com.ltmonitor.app.GpsConnection;
import com.ltmonitor.jt808.service.IGpsConnectionService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ltmonitor.dao.IBaseIbatisDao;
import com.ltmonitor.dao.ITerminalDao;
import com.ltmonitor.dao.IVehicleDataDao;
import com.ltmonitor.entity.GPSRealData;
import com.ltmonitor.entity.GpsInfo;
import com.ltmonitor.entity.Terminal;
import com.ltmonitor.entity.VehicleData;
import com.ltmonitor.jt808.service.IAlarmService;
import com.ltmonitor.jt808.service.IOnlineAnalyzeService;
import com.ltmonitor.service.IGPSRealDataService;
import com.ltmonitor.service.ILocationService;
import com.ltmonitor.service.IRealDataService;
import com.ltmonitor.service.ITerminalService;
import com.ltmonitor.service.IVehicleDataService;
import com.ltmonitor.util.DateUtil;
import com.ltmonitor.util.StringUtil;

/**
 * 实时数据服务,此服务作为RMI服务,由808server提供给webserver远程调用
 * 1.将实时数据缓存在内存中
 * 2.对外提供获取实时数据的接口
 * 3.定时生成地图位置描述信息
 * @author DELL
 * 
 */
@Service("realDataService")
public class RealDataService implements IRealDataService {

	private static Logger logger = Logger.getLogger(RealDataService.class);
	@Autowired
	IBaseIbatisDao baseIbatisDao;

	@Autowired
	IGPSRealDataService gpsRealDataService;
	
	@Autowired
	private IAlarmService alarmService;

	@Autowired
	private ITerminalService terminalService;

	
	@Autowired
	private IOnlineAnalyzeService onlineRecordService;
	@Autowired
	private IVehicleDataService vehicleDataService;

	public ConcurrentMap<String, GPSRealData> realDataMap = new ConcurrentHashMap<String, GPSRealData>();
	/**
	 * 存储当前终端的在线时间，key是simNo, value是在线时间
	 */
	public ConcurrentMap<String, Date> onlineMap = new ConcurrentHashMap<String, Date>();

	private HashMap<String, Date> updateMap = new HashMap<String, Date>();

	private ConcurrentMap<String, VehicleData> vehicleMap = new ConcurrentHashMap<String, VehicleData>();

	private ConcurrentMap<String, VehicleData> vehiclePlateNoMap = new ConcurrentHashMap<String, VehicleData>();

	private ConcurrentMap<String, Terminal> terminalMap = new ConcurrentHashMap<String, Terminal>();


	/**
	 * 开辟线程，定时更新实时数据入库
	 */
	private Thread processRealDataThread;

	/**
	 * 单独开辟一个线程，不断的更新实时数据的位置信息
	 */
	private Thread locationThread;

	/**
	 * 定时生成位置信息的服务
	 */
	@Autowired
	private ILocationService locationService;

	/**
	 * 最大离线时间,超过此时间，收不到包，将视为离线
	 */
	@Value("#{config.maxOfflineTime}")  
	private double maxOfflineTime = 60 * 1; 
	

	/**
	 * 最大离线时间,超过此时间，收不到包，将视为离线
	 */
	@Value("#{config.locationUpdateInterval}")  
	private long locationUpdateInterval = 30; 

	private boolean startUpdate = true;
	/**
	 * 保存设备的连接状态，暂时没有用
	 */
	private ConcurrentMap<String, Boolean> connectedStateMap = new ConcurrentHashMap<String, Boolean>();

	public RealDataService() {

	}
	/**
	 * spring加载类的时候，自动启动
	 */
	public void start() {
		this.initData();
		processRealDataThread = new Thread(new Runnable() {
			public void run() {
				ProcessRealDataThreadFunc();
			}
		});
		processRealDataThread.start();

		locationThread = new Thread(new Runnable() {
			public void run() {
				locationThreadFunc();
			}
		});
		locationThread.start();
		
	}
	/**
	 * 初始化缓存数据, 优化性能
	 */
	private void initData()
	{
		String hql = "from VehicleData where depId > 0 and deleted = false and simNo is not null and plateNo is not null";
		List<VehicleData> vehicleList = this.vehicleDataService.query(hql);
		for (VehicleData vd : vehicleList) {
			this.vehicleMap.put(vd.getSimNo(), vd);
			this.vehiclePlateNoMap.put(vd.getPlateNo(), vd);
		}

		hql = "from Terminal where termNo is not null and deleted = false";
		List<Terminal> termList = this.terminalService.query(hql);
		for (Terminal vd : termList) {
			this.terminalMap.put(vd.getTermNo(), vd);
		}

		hql = "from GPSRealData where online = ? and simNo is not null";
		List ls = this.gpsRealDataService.query(hql, true);
		for (Object obj : ls) {
			GPSRealData rd = (GPSRealData) obj;
			// rd.setSendTime(new Date());
			// rd.setOnlineDate(new Date());
			//this.isOnlineStateChanged(rd);
			rd.setOnline(false);
			this.realDataMap.put(rd.getSimNo(), rd);
			this.updateMap.put(rd.getSimNo(), rd.getSendTime());
			this.onlineMap.put(rd.getSimNo(), new Date());
			
		}
	}

	@Override
	public void stopService() {
		startUpdate = false;
		try {
			processRealDataThread.join(50000);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}
		try {
			locationThread.join(50000);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}
	}

	/**
	 * 调用mybatis的批量更新接口，批量更新实时数据
	 * 
	 * @param realDataList
	 */
	public void batchUpdate(List realDataList) {
		this.baseIbatisDao.batchUpdate("jt808server.updateRealData", realDataList);
	}

	private void locationThreadFunc() {
		while (startUpdate) {
			try {
				Date start = new Date();
				ArrayList<String> keys = new ArrayList(onlineMap.keySet());
				int size = 0;
				for (String simNo : keys) {
					GPSRealData rd = get(simNo);
					if (rd == null)
						continue;

					// 只更新上线的位置信息
					if (rd.getLatitude() > 0 && rd.getLongitude() > 0
							&& rd.getOnline()) {
						String location = locationService.getLocation(
								rd.getLatitude(), rd.getLongitude());
						if (StringUtil.isNullOrEmpty(location) == false) {
							rd.setLocation(location);
						}
						size++;
					}
				}
				if (size > 0) {
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			try {
				Thread.sleep(locationUpdateInterval * 1000L);
			} catch (InterruptedException e1) {
				logger.error(e1.getMessage(), e1);
			}
		}
	}

	private void ProcessRealDataThreadFunc() {
		while (startUpdate) {
			try {
				List<GPSRealData> result = new ArrayList<GPSRealData>();
				ArrayList<String> keys = null;
				keys = new ArrayList<String>(onlineMap.keySet());
				for (String simNo : keys) {
					GPSRealData rd = get(simNo);
					if (rd == null)
						continue;
					boolean changed = false;// isOnlineStateChanged(rd);
					Date lastUpdateDate = updateMap.get(simNo);
					if (changed == false && lastUpdateDate != null) {
						if (lastUpdateDate.compareTo(rd.getOnlineDate()) >= 0) {
							continue;
						}
					}
					

					if (rd.getID() == 0) {
						this.saveRealData(rd);
					} else {
						rd.setUpdateDate(new Date());
						result.add(rd);
					}

					updateMap.put(simNo, rd.getOnlineDate());
				}
				if (result.size() > 0) {
					batchUpdate(result);
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			try {
				Thread.sleep(1000L);
			} catch (InterruptedException e1) {
			}
		}
	}

	public void update(GPSRealData rd) {
		//rd.setOnlineDate(new Date());
		// rd.setOnline(true);
		onlineMap.put(rd.getSimNo(), rd.getOnlineDate());
		realDataMap.put(rd.getSimNo(), rd);
	}

	/**
	public void updateOnlineTime(GPSRealData rd, Date onlineDate) {
		if (rd == null)
			return;
		rd.setOnlineDate(onlineDate);
		onlineMap.put(rd.getSimNo(), onlineDate);
	}*/

	public void UpdateConnectedState(String simNo, Boolean isConnected) {
		this.connectedStateMap.put(simNo, isConnected);
	}

	public Boolean isConnected(String simNo) {
		if (connectedStateMap.containsKey(simNo)) {
			return connectedStateMap.get(simNo);
		}
		return false;
	}

	/**
	 * 判断在线状态
	 * @param rd
	 * @return
	 */
	private Boolean isOnlineStateChanged(GPSRealData rd) {
		boolean online = false;

		//GpsConnection gc = this.gpsConnectionService.getConnection(rd.getSimNo());

		Date onlineTime = rd.getOnlineDate();
		double maxTime = maxOfflineTime;
		if (onlineTime != null) {
			double ts = DateUtil.getSeconds(onlineTime, new Date());
			online = (ts < maxTime);
		}
		boolean changed = rd.getOnline() != online;
		if (changed) {
			rd.setOnline(online);
			//gc.setVehicleOnline(online);
			//if (online)
				//rd.setSendTime(onlineTime);
			//在线状态发生变化的时候，创建上下线记录
			onlineRecordService.checkOnline(rd);
		}
		return changed;
	}

	public List<GPSRealData> getRealDataList(List<String> simNoList) {
		List<GPSRealData> result = new ArrayList<GPSRealData>();
		for (String simNo : simNoList) {
			GPSRealData rd = get(simNo);
			if (rd != null)
				result.add(rd);
		}
		return result;
	}

	/**
	 * 获取在线的实时数据，用于webserver批量获取
	 */
	public List<GPSRealData> getOnlineRealDataList() {
		List<GPSRealData> result = new ArrayList<GPSRealData>();
		Set<String> keys = onlineMap.keySet();
		for (String simNo : keys) {
			GPSRealData rd = get(simNo);
			if (rd != null)
				result.add(rd);
		}
		return result;
	}

	public void saveRealData(GPSRealData rd) {
		try {			
			this.gpsRealDataService.saveOrUpdate(rd);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	//@Transactional(readOnly=true)
	public GPSRealData get(String simNo) {
		GPSRealData rd = null;
		if (realDataMap.containsKey(simNo)) {
			rd = realDataMap.get(simNo);
			if(rd == null || rd.getSimNo() == null)
				return null;
		} else {
			String hsql = "from GPSRealData gps where gps.simNo= ? ";
			//logger.error("808 before get:" + simNo);
			rd = (GPSRealData)this.gpsRealDataService.find(hsql, simNo);
			//logger.error("808 end get:" + simNo);
			if (rd == null) {
				VehicleData vd = this.getVehicleData(simNo);
				if (vd == null) {
					//realDataMap.put(simNo, new GPSRealData());
					return null; 
				}

				if (vd != null) {
					rd = new GPSRealData();
					rd.setSimNo(simNo);
					rd.setVehicleId(vd.getEntityId());
					rd.setPlateNo(vd.getPlateNo());
					rd.setDepId(vd.getDepId());
					try {
						// saveRealData(rd);
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
					}
				}
			}
			rd.setOnline(false);
			rd.setValid(false);
			if (StringUtil.isNullOrEmpty(rd.getLocation())
					&& rd.getLatitude() > 0 && rd.getLongitude() > 0) {

				// this.saveRealData(rd);
			}
			realDataMap.put(simNo, rd);
		}

		if (rd.getOnlineDate() == null)
			rd.setOnlineDate(rd.getSendTime());
		return rd;
	}

	/**
	private void setLocation(Object obj) {
		String simNo = "" + obj;
		GPSRealData rd = this.get(simNo);
		String location = locationService.getLocation(rd.getLatitude(),
				rd.getLongitude());
		if (StringUtil.isNullOrEmpty(location) == false) {
			rd.setLocation(location);
		}
	}*/

	/**
	 * 给808server的其他service提供获取车辆信息的方法，从缓存中获取，可以提高性能
	 */
	public VehicleData getVehicleData(String simNo) {
		if (vehicleMap.containsKey(simNo) == false) {
			String hql = "from VehicleData v where v.simNo =  ? and deleted = ?";

			VehicleData v = vehicleDataService.find(hql,
					new Object[] { simNo, false });
			if (v != null)
				vehicleMap.put(simNo, v);
			return v;
		} else {
			VehicleData v = vehicleMap.get(simNo);
			if (v.getDeleted())
				return null;
			return v;
		}
	}

	/**
	 * 此方法提供给web端调用，用于808server和webserver之间的缓存同步
	 * 如果车辆静态信息发生变化，则更新缓存中的车辆信息
	 * 
	 * @param simNo
	 * @param v
	 */
	public void updateVehicleData(String simNo, VehicleData v) {
		vehicleMap.put(v.getSimNo(), v);
		vehiclePlateNoMap.put(v.getPlateNo(), v);
		if (simNo.equals(v.getSimNo()) == false) {
			vehicleMap.remove(simNo);
			this.realDataMap.remove(simNo);
		}
		//如果是删除车辆，则需要移除缓存
		if (v.getDeleted()) {
			vehicleMap.remove(v.getSimNo());
			this.realDataMap.remove(v.getSimNo());
			this.vehiclePlateNoMap.remove(v.getPlateNo());
		}
		GPSRealData rd = null;
		if(simNo.equals(v.getSimNo()) == false && realDataMap.containsKey(simNo))
		{
			rd = realDataMap.get(simNo);
			if(rd != null && rd.getSimNo() == null)
			{
				realDataMap.remove(simNo);
				rd = null;
			}
		}
		if(rd == null)
		     rd = this.get(v.getSimNo());
		
		
		if (rd != null) {
			rd.setPlateNo(v.getPlateNo());
			rd.setVehicleId(v.getEntityId());

		}
	}



	@Override
	public void saveHisGpsInfo(GpsInfo gi) {
		// TODO Auto-generated method stub

	}

	public VehicleData getVehicleByPlateNo(String plateNo) {
		return vehiclePlateNoMap.get(plateNo);
	}

	public Terminal getTerminalByTermNo(String termNo) {
		Terminal t = this.terminalMap.get(termNo);
		if (t == null) {
			String hql = "from Terminal where termNo = ? and deleted = ?";
			if (t != null) {
				t = (Terminal) this.terminalService.find(hql, new Object[] { termNo,
						false });
				this.terminalMap.put(termNo, t);
			}
		}
		return t;
	}


	public void updateTerminal(String termNo, Terminal t)
	{
		if(t.getDeleted() == false)
			this.terminalMap.put(t.getTermNo(), t);
		else
			this.terminalMap.remove(t.getTermNo());
		if(termNo.equals(t.getTermNo()) == false)
		{
			this.terminalMap.remove(termNo);
		}
	}



}
